#if _MSC_VER
 #pragma once
#endif

#ifndef __HERZELEID_CORE_INCLUDED__
#define __HERZELEID_CORE_INCLUDED__
#include <d3d11_1.h>
#include <sstream>
#include <string>

#define DECLSPEC_INTRIN		__declspec(intrin_type)
#define DECLSPEC_ALIGN(x)	__declspec(align(x)) // Alignes type 

#define IfThenElse( Condition, Then, Else ) ((Condition) ? (Then) : (Else)) // Shortcut for <Condition> ? <Then> : <Else> structure. 
#define MaxOf( X, Y ) ((X) > (Y) ? (X) : (Y)) // Gets max value
#define MinOf( X, Y ) ((X) < (Y) ? (X) : (Y)) // Gets min value
#define AbsOf( X ) ((X) < 0 ? (-X) : (X)) // Gets absolute value 


#define event												mutable
#define null												nullptr
#define pure												PURE
#define Undefined_Slot										(-1)
#define HERZELEID_NOALIAS									__declspec(noalias)										
#define HERZELEID_ALIGN( Bytes )							__declspec(align(Bytes))										
#define HERZELEID_HINTINLINING								inline										
#define HERZELEID_FORCEINLINING								__forceinline										
#define HERZELEID_METHODSTDCALLTYPE							_stdcall										

#define NoFlags												(0U)
#define BitMask( Pos )										(1 << Pos)

#define HERZELEID_DEFAULT_THREAD_SLOT						(0U)
#define HERZELEID_KEYBOARD_KEYS_NUM_SLOTS					(256)
#define HERZELEID_THREAD_NUM_SLOTS							(4)
#define HERZELEID_VERTEX_BUFFER_NUM_SLOTS					D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT
#define HERZELEID_CBUFFER_NUM_SLOTS							D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT
#define HERZELEID_SAMPLER_NUM_SLOTS							D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT
#define HERZELEID_SRV_NUM_SLOTS								D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT
#define HERZELEID_UAV_NUM_SLOTS								D3D11_PS_CS_UAV_REGISTER_COUNT
#define HERZELEID_VIEWPORT_NUM_SLOTS						D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE
#define HERZELEID_SCISSOR_RECT_NUM_SLOTS					D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE
#define HERZELEID_RTV_NUM_SLOTS								D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT
#define HERZELEID_DESIGN_SCALE_DPI_PIXELS					(96.0F)
#define HERZELEID_DEFAULT_DXGI_FORMAT						(DXGI_FORMAT::DXGI_FORMAT_B8G8R8A8_UNORM)
#define HERZELEID_DEFAULT_INDEX_BUFFER_DXGI_FORMAT			(DXGI_FORMAT::DXGI_FORMAT_R32_UINT)
#define HERZELEID_OM_DEFAULT_SAMPLE_MASK					(0XFFFFFFFFU)

#define Is_Undefined_Slot( Id )								( Id == Undefined_Slot )
#define Is_Valid_VertexBuffer_Slot( iSlot )					( ((uint32_t)iSlot) < HERZELEID_VERTEX_BUFFER_NUM_SLOTS )
#define Is_Valid_ConstantBuffer_Slot( iSlot )				( ((uint32_t)iSlot) < HERZELEID_CBUFFER_NUM_SLOTS )
#define Is_Valid_Sampler_Slot( iSlot )						( ((uint32_t)iSlot) < HERZELEID_SAMPLER_NUM_SLOTS )
#define Is_Valid_SRV_Slot( iSlot )							( ((uint32_t)iSlot) < HERZELEID_SRV_NUM_SLOTS )
#define Is_Valid_UAV_Slot( iSlot )							( ((uint32_t)iSlot) < HERZELEID_UAV_NUM_SLOTS )
#define Is_Valid_Viewport_Slot( iSlot )						( ((uint32_t)iSlot) < HERZELEID_VIEWPORT_NUM_SLOTS )
#define Is_Valid_ScissorRect_Slot( iSlot )					( ((uint32_t)iSlot) < HERZELEID_SCISSOR_RECT_NUM_SLOTS )
#define Is_Valid_RenderTarget_Slot( iSlot )					( ((uint32_t)iSlot) < HERZELEID_RENDER_TARGET_NUM_SLOTS )

#define Assert_Thread_Slot( iSlot )							{ assert( ((uint32_t)iSlot) < HERZELEID_THREAD_NUM_SLOTS + 1 ); }
#define InfiniteLoop										for ( ; ; ) // Good luck!

#define Foreach_Thread_Slot( iSlot )						for ( int32_t iSlot = 0; iSlot < HERZELEID_THREAD_NUM_SLOTS + 1; iSlot++ )
#define Foreach_DeferredThread_Slot( iSlot )				for ( int32_t iSlot = 0; iSlot < HERZELEID_THREAD_NUM_SLOTS; iSlot++ )

#define Foreach_VertexBuffer_Slot( iSlot )					for ( int32_t iSlot = 0; iSlot < HERZELEID_VERTEX_BUFFER_NUM_SLOTS; iSlot++ )
#define Foreach_ConstantBuffer_Slot( iSlot )				for ( int32_t iSlot = 0; iSlot < HERZELEID_CBUFFER_NUM_SLOTS; iSlot++ )
#define Foreach_Sampler_Slot( iSlot )						for ( int32_t iSlot = 0; iSlot < HERZELEID_SAMPLER_NUM_SLOTS; iSlot++ )
#define Foreach_SRC_Slot( iSlot )							for ( int32_t iSlot = 0; iSlot < HERZELEID_SRV_NUM_SLOTS; iSlot++ )
#define Foreach_UAV_Slot( iSlot )							for ( int32_t iSlot = 0; iSlot < HERZELEID_UAV_NUM_SLOTS; iSlot++ )
#define Foreach_Viewport_Slot( iSlot )						for ( int32_t iSlot = 0; iSlot < HERZELEID_VIEWPORT_NUM_SLOTS; iSlot++ )
#define Foreach_ScissorRect_Slot( iSlot )					for ( int32_t iSlot = 0; iSlot < HERZELEID_SCISSOR_RECT_NUM_SLOTS; iSlot++ )
#define Foreach_RenderTarget_Slot( iSlot )					for ( int32_t iSlot = 0; iSlot < HERZELEID_RENDER_TARGET_NUM_SLOTS; iSlot++ )

#define Foreach_VertexBuffer_Slot_Reversed( iSlot )			for ( int32_t iSlot = HERZELEID_VERTEX_BUFFER_NUM_SLOTS - 1; iSlot >= 0; iSlot-- )
#define Foreach_ConstantBuffer_Slot_Reversed( iSlot )		for ( int32_t iSlot = HERZELEID_CBUFFER_NUM_SLOTS - 1; iSlot >= 0; iSlot-- )
#define Foreach_Sampler_Slot_Reversed( iSlot )				for ( int32_t iSlot = HERZELEID_SAMPLER_NUM_SLOTS - 1; iSlot >= 0; iSlot-- )
#define Foreach_SRV_Slot_Reversed( iSlot )					for ( int32_t iSlot = HERZELEID_SRV_NUM_SLOTS - 1; iSlot >= 0; iSlot-- )
#define Foreach_UAV_Slot_Reversed( iSlot )					for ( int32_t iSlot = HERZELEID_UAV_NUM_SLOTS - 1; iSlot >= 0; iSlot-- )

#define IsFlagChecked( Flags, Flag )						( (Flags & Flag) == Flag )
#define GetUuidOf( T )										( __uuidof( T ) )
#define GetByteSizeOf( x )									( sizeof(x) )
#define GetBitSizeOf( x )									( GetByteSizeOf(x) * 8 )

#ifdef HERZELEID_DESKTOP
#define Safe_Delete( x )									{ if ( x ) { delete (x); (x) = null; } } // Ptr
#define Safe_DeleteArray( x )								{ if ( x ) { delete[] (x); (x) = null; } } // Dynamically allocated Array
#define Safe_Release( x )									{ if ( x ) { (x)->Release(); (x) = null; } } // Com
#define Safe_Destroy( x )									{ if ( x ) { (x)->Destroy(); (x) = null; } } // Fbx
#else
#define Safe_Delete( x ) 
#define Safe_DeleteArray( x ) 
#define Safe_Release( x )
#define Safe_Destroy( x )									{ if ( x ) { (x)->Destroy(); (x) = null; } } // Fbx
#endif

#ifdef HERZELEID_DESKTOP // Properties

// Declare Read Write Property
#define DeclGetSetPropOnly( Type, Name )  __declspec( property ( put = PropertySetterFor##Name, get = PropertyGetterFor##Name ) ) Type Name; typedef Type PropertyTypeOf##Name; 
// Declare Read Property
#define DeclGetPropOnly( Type, Name ) __declspec( property (get = PropertyGetterFor##Name) ) Type Name; typedef Type PropertyTypeOf##Name; 
// Declare Write Property
#define DeclSetPropOnly( Type, Name ) __declspec( property (put = PropertySetterFor##Name) ) Type Name; typedef Type PropertyTypeOf##Name;

// Declare Read Write Property
#define DeclStaticGetSetPropOnly( Type, Name )  __declspec( property ( put = PropertySetterFor##Name, get = PropertyGetterFor##Name ) ) static Type Name; typedef Type PropertyTypeOf##Name; 
// Declare Read Property
#define DeclStaticGetPropOnly( Type, Name ) __declspec( property (get = PropertyGetterFor##Name) ) static Type Name; typedef Type PropertyTypeOf##Name; 
// Declare Write Property
#define DeclStaticSetPropOnly( Type, Name ) __declspec( property (put = PropertySetterFor##Name) ) static Type Name; typedef Type PropertyTypeOf##Name;



// Property GetFunction Body 
#define DeclGetterFor( Name ) PropertyTypeOf##Name const PropertyGetterFor##Name( ) 
#define DeclTypedGetterFor( Type, Name ) Type PropertyGetterFor##Name( ) 
#define DeclConstTypedGetterFor( Type, Name ) Type PropertyGetterFor##Name( ) const
// Property SetFunction Body 
#define DeclSetterFor( Name ) void PropertySetterFor##Name( PropertyTypeOf##Name const &ValueRef )  
#define DeclTypedSetterFor( Type, Name ) void PropertySetterFor##Name( Type Value )  

// Property GetFunction Body 
#define DeclStaticGetterFor( Name ) static PropertyTypeOf##Name const PropertyGetterFor##Name( ) 
#define DeclStaticTypedGetterFor( Type, Name ) static Type PropertyGetterFor##Name( ) 
// Property SetFunction Body 
#define DeclStaticSetterFor( Name ) static void PropertySetterFor##Name( PropertyTypeOf##Name const &ValueRef )  
#define DeclStaticTypedSetterFor( Type, Name ) static void PropertySetterFor##Name( Type Value )  

// Declare Read Write Property With Get/Set Functions Declarations
#define DeclGetSetProp( Type, Name )  DeclGetSetPropOnly( Type, Name ); DeclGetterFor( Name );  DeclSetterFor( Name ); 
// Declare Read Property With Get Function Declaration
#define DeclGetProp( Type, Name ) DeclGetPropOnly( Type, Name ); DeclGetterFor( Name ); 
// Declare Write Property With Set Function Declaration
#define DeclSetProp( Type, Name ) DeclSetPropOnly( Type, Name ); DeclSetterFor( Name );  

// Property GetFunction Body 
#define DeclVirtualGetterFor( Name ) virtual PropertyTypeOf##Name const PropertyGetterFor##Name( ) 
// Property SetFunction Body 
#define DeclVirtualSetterFor( Name ) virtual void PropertySetterFor##Name( PropertyTypeOf##Name const &ValueRef)  

// Property GetFunction Body 
#define DeclPureVirtualGetterFor( Name ) virtual PropertyTypeOf##Name const PropertyGetterFor##Name( ) pure
// Property SetFunction Body 
#define DeclPureVirtualSetterFor( Name ) virtual void PropertySetterFor##Name( PropertyTypeOf##Name const &ValueRef) pure

// Declare Read Write Property With Get/Set Functions Declarations
#define DeclVirtualGetSetProp( Type, Name )  DeclGetSetPropOnly( Type, Name ); DeclVirtualGetterFor( Name );  DeclVirtualSetterFor( Name ); 
// Declare Read Property With Get Function Declaration
#define DeclVirtualGetProp( Type, Name ) DeclGetPropOnly( Type, Name ); DeclVirtualGetterFor( Name ); 
// Declare Write Property With Set Function Declaration
#define DeclVirtualSetProp( Type, Name ) DeclSetPropOnly( Type, Name ); DeclVirtualSetterFor( Name ); 

#endif // !Properties


//#ifdef HERZELEID_DESKTOP
// Declares most common custom type usage definitions 
#define TinyTypeDecl(Type) \
	typedef Type *Type##Ptr; \
	typedef Type &Type##Ref; \
	typedef Type **Type##Address; \
	typedef Type *&Type##Ptr##Ref; \
	typedef Type const Type##Const; \
	typedef Type const *Type##Const##Ptr; \
	typedef Type const &Type##Const##Ref; \
	typedef Type *const Type##Ptr##Const; \
	typedef Type const *const Type##Const##Ptr##Const;
//#else 
//#define TinyTypeDecl(Type) // Noop
//#endif

// Declares most common pod-type usage definitions 
#define TypeDecl(StandardTypeName, PredefinedTypeName, DesiredTypeName) \
	typedef StandardTypeName DesiredTypeName; \
	typedef StandardTypeName PredefinedTypeName##_##t; \
	typedef PredefinedTypeName##_##t PredefinedTypeName##_##t; \
	typedef PredefinedTypeName##_##t *PredefinedTypeName##_##ptr_##t, *DesiredTypeName##Ptr; \
	typedef PredefinedTypeName##_##t &PredefinedTypeName##_##ref_##t, &DesiredTypeName##Ref; \
	typedef PredefinedTypeName##_##t const PredefinedTypeName##_##const_##t, DesiredTypeName##Const; \
	typedef PredefinedTypeName##_##t **PredefinedTypeName##_##address_##t, **DesiredTypeName##Address; \
	typedef PredefinedTypeName##_##t *&PredefinedTypeName##_##ptr_##ref_##t, *&DesiredTypeName##PtrRef; \
	typedef PredefinedTypeName##_##t const *PredefinedTypeName##_##const_##ptr_##t, *DesiredTypeName##Const##Ptr; \
	typedef PredefinedTypeName##_##t const &PredefinedTypeName##_##const_##ref_##t, &DesiredTypeName##Const##Ref; \
	typedef PredefinedTypeName##_##t *const PredefinedTypeName##_##ptr_##const_##t, *const DesiredTypeName##Ptr##Const; \
	typedef PredefinedTypeName##_##t const *const PredefinedTypeName##_##const_##ptr_##const_##t, *const DesiredTypeName##Const##Ptr##Const;

#if HERZELEID_DEBUG
#define PrintToDebugWindowOnly16( wszString ) { OutputDebugStringW( wszString ); OutputDebugStringW( L"\n" ); } // Does exactly what it says. 
 #define PrintToDebugWindowOnly8( szString )  { OutputDebugStringA( szString ); OutputDebugStringA( "\n" ); } // Does exactly what it says. 
 #if defined(_UNICODE) || defined(UNICODE)
  #define PrintToDebugWindowOnly( wszString ) PrintToDebugWindowOnly16(wszString) // Does exactly what it says. 
 #else
  #define PrintToDebugWindowOnly( szString ) PrintToDebugWindowOnly8 // Does exactly what it says. 
 #endif
#else // Noop in release version. 
 #define PrintToDebugWindowOnly( szString ) 
#endif

#ifdef HERZELEID_DEBUG 
// We want to the know the returned value only when debugging. Take the evaluated expression in brackets. 
// Please ensure you dont use this variable when releasing the project. 
#define TypedDebugVariableWithCast( Type, Name ) Type Name = (Type)
// We want to the know the returned value only when debugging. 
// Please ensure you dont use this variable when releasing the project. 
#define TypedDebugVariable( Type, Name ) Type Name = 
// We want to the know the returned value only when debugging. 
// Please ensure you dont use this variable when releasing the project. 
#define AutoDebugVariable( Name ) auto Name = 
#else // Noops
#define TypedDebugVariableWithCast( Type, Name )
#define TypedDebugVariable( Type, Name )		
#define AutoDebugVariable( Name )				
#endif

// Sehnsucht Engine 
namespace Application
{
	// Standard types
	namespace Standard
	{
		typedef void Void, Object, *VoidPtr, *const VoidPtrConst, 
			*object_ptr_t, *handle_t, 
			*ObjectPtr, *Handle;

		TypeDecl( HINSTANCE, hinstance, InstanceHandle );	// App instance handle
		TypeDecl( HRESULT, hresult, HResult );				// Window message first parameter
		TypeDecl( LRESULT, lresult, LResult );				// Window message first parameter
		TypeDecl( HWND, hwnd, WindowHandle );				// Window handle
		TypeDecl( WPARAM, wparam, WParam );					// Window message first parameter
		TypeDecl( LPARAM, lparam, LParam );					// Window message second parameter
		
		TypeDecl( double, __float64, Float64 );				// Double (8bytes)
		TypeDecl( float, __float32, Float32 );				// Single (4bytes)

		TypeDecl( bool, __bool, Bool );						// Boolean (4bytes)
		TypeDecl( char, __char8, Char8 );					// Char (1byte)
		TypeDecl( wchar_t, __char16, Char16 );				// WideChar (2bytes)
		TypeDecl( unsigned char, __uchar8, UChar8 );		// Unsigned Char (1byte)
		TypeDecl( unsigned short, __uchar16, UChar16 );		// Unsigned WideChar (2bytes)

		TypeDecl( __int64, __int64, Int64);					// LongLong (8bytes)
		TypeDecl( __int32, __int32, Int32);					// Int (4bytes)
		TypeDecl( __int16, __int16, Int16);					// Short (2bytes)
		TypeDecl( __int8, __int8, Int8);					// Char (1byte)

		TypeDecl( unsigned __int64, __uint64, UInt64);		// QWORD (8bytes)
		TypeDecl( unsigned __int32, __uint32, UInt32);		// DWORD (4bytes)
		TypeDecl( unsigned __int16, __uint16, UInt16);		// WORD (2bytes)
		TypeDecl( unsigned __int8, __uint8, UInt8);			// BYTE (1byte)

		TypeDecl( unsigned __int64, __qword, QWord);		// QWORD (8bytes)
		TypeDecl( unsigned __int32, __dword, DWord);		// DWORD (4bytes)
		TypeDecl( unsigned __int16, __word, Word);			// WORD (2bytes)
		TypeDecl( unsigned __int8, __byte, Byte);			// BYTE (1byte)

		typedef std::stringstream	StringStream8;
		typedef std::wstringstream	StringStream16;
		typedef std::ofstream		OutputFileStream8;
		typedef std::wofstream		OutputFileStream16;

		TypeDecl(std::wstring, string16, String16);				// String (2bytes)
		TypeDecl(std::string, string8, String8);				// String (1byte)

#if defined(_UNICODE) || defined(UNICODE)
		typedef Char16 Char; 
		typedef String16 String; 
		typedef StringStream16 StringStream; 
#else
		typedef Char8 Char; 
		typedef String8 String; 
		typedef StringStream8 StringStream; 
#endif

		// Forces the type to implement Release() method. 
		struct IReleasee
		{
			// Destroys internally allocated resources.
			virtual void Release() = 0;

		};
		
		template<class U>
		// Zeros mem of the pod instance (shortcut for the ZeroMemory define). Can work for the array as well (use ZeroValueArray for pod pointers). 
		// \note Its not the delete or delete[]. In these cases consider using defines Safe_Delete, Safe_DeleteArray, MemoryPools etc. 
		inline void ZeroValue( _In_ U *pValue, _In_opt_ size_t Size = sizeof(U) )
		{
			ZeroMemory( pValue, Size );
		}

		template<class U>
		// Zeros mem of the pod instances (params are pretty much obvious)
		// \note Its not delete[]. In these cases consider using define Safe_DeleteArray. 
		static inline void ZeroValueArray( _In_ U *pValue, _In_ size_t NumElemnts, _In_ size_t ElementSize = sizeof(U) )
		{
			ZeroMemory( pValue, (NumElemnts * ElementSize) );
		}

		// \note Microsoft::WRL::ComPtr<T> can be utilized to avoid manual com pointer handling.
		// \note Safe_Release can be used as well 
		inline void Release( _In_ IUnknown *pValue )
		{
			if (pValue)
			{
				pValue->Release();
				pValue = 0;
			}
		}
	};
};

using namespace Application::Standard;

#ifdef HERZELEID_DESKTOP
 #define ref // For class definition compability
 #define internal public // For public constructor definition compability
 #pragma warning(disable:4005)
  #define interface // For interface classes compability
 #pragma warning(default:4005)
 #define pure PURE
#else // !HERZELEID_DESKTOP
 #define pure
#endif // HERZELEID_DESKTOP

#ifdef HERZELEID_DESKTOP
namespace Platform
{
	typedef Application::Standard::ObjectPtr Object;
	typedef Application::Standard::String16 String;
	typedef Application::Standard::String16 *StringPtr;
	typedef std::shared_ptr<String16> StringArg;
	template <class U> class Array;
};
typedef Platform::Array<Platform::StringArg> StringPtrArray;
typedef Platform::Array<Platform::StringArg>* StringPtrArrayPtr;
typedef std::shared_ptr<Platform::Array<Platform::StringArg>> SharedStringArray;
#else
namespace Platform
{
	typedef Platform::String^ StringArg;
};
typedef Platform::Array<Platform::String^>^ SharedStringArray;
#endif // !HERZELEID_DESKTOP

#endif //!HERZELEID_CORE_INCLUDED__
